package com.ruicar.afs.cloud.bizcommon.rules.service.impl;

import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.thread.ThreadUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ql.util.express.DefaultContext;
import com.ql.util.express.ExpressRunner;
import com.ruicar.afs.cloud.bizcommon.dto.rules.atom.AfsRuleCopyDto;
import com.ruicar.afs.cloud.bizcommon.dto.rules.atom.AfsRuleInfoDto;
import com.ruicar.afs.cloud.bizcommon.dto.rules.tree.*;
import com.ruicar.afs.cloud.bizcommon.enums.rules.*;
import com.ruicar.afs.cloud.bizcommon.rules.component.RulePublishComponent;
import com.ruicar.afs.cloud.bizcommon.rules.entity.*;
import com.ruicar.afs.cloud.bizcommon.rules.mapper.*;
import com.ruicar.afs.cloud.bizcommon.rules.service.AfsRuleInfoService;
import com.ruicar.afs.cloud.bizcommon.rules.service.AfsRuleOperateHisService;
import com.ruicar.afs.cloud.bizcommon.rules.service.AfsRulePublishService;
import com.ruicar.afs.cloud.bizcommon.rules.util.RuleConfigUtil;
import com.ruicar.afs.cloud.common.core.constant.CommonConstants;
import com.ruicar.afs.cloud.common.core.enums.AfsEnumUtil;
import com.ruicar.afs.cloud.common.core.exception.AfsBaseException;
import com.ruicar.afs.cloud.common.core.service.ServiceInfo;
import com.ruicar.afs.cloud.common.core.uid.UidGenerator;
import com.ruicar.afs.cloud.common.core.util.SpringContextHolder;
import com.ruicar.afs.cloud.common.rules.constants.RuleConstants;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;

import java.text.MessageFormat;
import java.util.*;
import java.util.concurrent.Callable;
import java.util.concurrent.Future;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * AfsRuleServiceImpl
 * <p>Description: </p>
 *
 * @author Fzero
 * @version 1.0
 * @date create on 2020-05-15 23:57:19
 * @since 1.0
 */
@Service
@Slf4j
@AllArgsConstructor
public class AfsRuleInfoServiceImpl extends ServiceImpl<AfsRuleInfoMapper, AfsRuleInfo> implements AfsRuleInfoService {
    private final StringRedisTemplate stringRedisTemplate;
    /**
     * Afs rule item mapper
     * <p>Description: </p>
     */
    private final AfsRuleItemMapper afsRuleItemMapper;
    /**
     * Afs rule express mapper
     * <p>Description: </p>
     */
    private final AfsRuleExpressMapper afsRuleExpressMapper;
    /**
     * Afs rule express atom mapper
     * <p>Description: </p>
     */
    private final AfsRuleExpressAtomMapper afsRuleExpressAtomMapper;
    /**
     * Afs rule input mapping mapper
     * <p>Description: </p>
     */
    private final AfsRuleInputMappingMapper afsRuleInputMappingMapper;
    /**
     * Afs rule trans express mapper
     * <p>Description: </p>
     */
    private final AfsRuleTransExpressMapper afsRuleTransExpressMapper;

    private final AfsRuleResultMappingMapper afsRuleResultMappingMapper;

    /**
     * Uid generator
     * <p>Description: </p>
     */
    private final UidGenerator uidGenerator;


    /**
     * Afs rule operate his service
     * <p>Description: </p>
     */
    private final AfsRuleOperateHisService afsRuleOperateHisService;


    private final AfsRulePublishService afsRulePublishService;

    /**
     * Save afs rule item *
     *
     * @param ruleTreeDto rule tree dto
     * @param ruleId      rule id
     * @param parentId    parent id
     * @param atomDtoMap  atom dto map
     */
    private void processAfsRuleItem(RuleTreeDto ruleTreeDto, Long ruleId, Long parentId, Map<String, RuleTreeExpressAtomDto> atomDtoMap, boolean saveToDb) {
        if (ruleTreeDto.getRows() == null)
            return;
        for (int i = 0; i < ruleTreeDto.getRows().size(); i++) {
            AfsRuleItem afsRuleItem = new AfsRuleItem();
            afsRuleItem.setRuleId(ruleId);
            afsRuleItem.setItemType(ruleTreeDto.getRows().get(i).getType());
            afsRuleItem.setItemLabel(ruleTreeDto.getRows().get(i).getLabel());
            afsRuleItem.setItemKey(ruleTreeDto.getRows().get(i).getKey());
            afsRuleItem.setItemNot(ruleTreeDto.getRows().get(i).isNot());
            afsRuleItem.setItemSortOrder(i);
            afsRuleItem.setParentId(parentId);
            if (saveToDb) {
                afsRuleItemMapper.insert(afsRuleItem);
            }
            processItemExpress(ruleTreeDto.getRows().get(i).getExpress(), ruleId, afsRuleItem.getId(), atomDtoMap, saveToDb);
            if (ruleTreeDto.getRows().size() > 0) {
                processAfsRuleItem(ruleTreeDto.getRows().get(i), ruleId, afsRuleItem.getId(), atomDtoMap, saveToDb);
            }
        }
    }

    /**
     * Process item express afs rule express
     * <p>Description: </p>
     *
     * @param ruleTreeExpressDto rule tree express dto
     * @param ruleId             rule id
     * @param itemId             item id
     * @param atomDtoMap         atom dto map
     * @return the afs rule express
     */
    private AfsRuleExpress processItemExpress(RuleTreeExpressDto ruleTreeExpressDto, Long ruleId, Long itemId, Map<String, RuleTreeExpressAtomDto> atomDtoMap, boolean saveToDb) {
        if (ruleTreeExpressDto == null) {
            return null;
        }
        AfsRuleExpress save = new AfsRuleExpress();
        save.setId(uidGenerator.getUID());
        save.setRuleId(ruleId);
        save.setItemId(itemId);
        save.setLeftType(ruleTreeExpressDto.getLeft().getType());
        save.setLeftReferenceId(null);
        save.setOperatorInfo(JSON.toJSONString(ruleTreeExpressDto.getOperator()));
        save.setRightType(ruleTreeExpressDto.getRight().getType());
        save.setRightReferenceId(null);
        save.setRightValue(ruleTreeExpressDto.getRight().getValue());
        save.setRightValueNames(ruleTreeExpressDto.getRight().getValueNames());
        if (RuleTreeExpressType.EXPRESS == ruleTreeExpressDto.getLeft().getType()) {
            AfsRuleExpress left = processItemExpress(ruleTreeExpressDto.getLeft().getExpress(), ruleId, itemId, atomDtoMap, saveToDb);
            save.setLeftReferenceId(left.getId());
        } else if (RuleTreeExpressType.ATOM == ruleTreeExpressDto.getLeft().getType()) {
            AfsRuleExpressAtom afsRuleExpressAtom = new AfsRuleExpressAtom();
            afsRuleExpressAtom.setId(uidGenerator.getUID());
            afsRuleExpressAtom.setExpressId(save.getId());
            afsRuleExpressAtom.setRuleId(ruleId);
            afsRuleExpressAtom.setAtomLocation(AtomLocation.LEFT);
            afsRuleExpressAtom.setAtomInfo(JSON.toJSONString(ruleTreeExpressDto.getLeft().getAtom()));
            if (saveToDb) {
                afsRuleExpressAtomMapper.insert(afsRuleExpressAtom);
            }
            atomDtoMap.put(ruleTreeExpressDto.getLeft().getAtom().getKey(), ruleTreeExpressDto.getLeft().getAtom());
        }

        if (RuleTreeExpressType.EXPRESS == ruleTreeExpressDto.getRight().getType()) {
            AfsRuleExpress right = processItemExpress(ruleTreeExpressDto.getRight().getExpress(), ruleId, itemId, atomDtoMap, saveToDb);
            save.setRightReferenceId(right.getId());
        } else if (RuleTreeExpressType.ATOM == ruleTreeExpressDto.getRight().getType()) {
            AfsRuleExpressAtom afsRuleExpressAtom = new AfsRuleExpressAtom();
            afsRuleExpressAtom.setId(uidGenerator.getUID());
            afsRuleExpressAtom.setExpressId(save.getId());
            afsRuleExpressAtom.setRuleId(ruleId);
            afsRuleExpressAtom.setAtomLocation(AtomLocation.RIGHT);
            afsRuleExpressAtom.setAtomInfo(JSON.toJSONString(ruleTreeExpressDto.getRight().getAtom()));
            if (saveToDb) {
                afsRuleExpressAtomMapper.insert(afsRuleExpressAtom);
            }
            atomDtoMap.put(ruleTreeExpressDto.getRight().getAtom().getKey(), ruleTreeExpressDto.getRight().getAtom());
        }
        if (saveToDb) {
            afsRuleExpressMapper.insert(save);
        }
        return save;
    }

    @Override
    public AfsRuleInfoDto checkRule(AfsRuleInfoDto afsRuleInfoDto) {
        List<RuleTreeDto> ruleTree = RuleConfigUtil.genRuleItemTree(afsRuleInfoDto.getRuleData());
        Assert.isTrue(ruleTree != null && ruleTree.size() > 0, "规则内容为空");

        Map<String, RuleTreeExpressAtomDto> atomDtoMap = new HashMap<>();
        Map<String, String> atomKeyMapping = new HashMap<>();
        DefaultContext context = new DefaultContext();
        for (int i = 0; i < ruleTree.size(); i++) {
            processItemExpress(ruleTree.get(i).getExpress(), null, null, atomDtoMap, true);
            processAfsRuleItem(ruleTree.get(i), null, null, atomDtoMap, true);
        }
        atomDtoMap.entrySet().forEach(entry -> {
            atomKeyMapping.put(entry.getKey(),"R_"+entry.getKey().replaceAll("\\.", "_") + "_" + RandomStringUtils.randomAlphanumeric(10));
            if (entry.getValue().getComponent() == AtomComponentType.NUMBER) {
                context.put(atomKeyMapping.get(entry.getKey()), 1000);
            } else if (entry.getValue().getComponent() == AtomComponentType.DATE) {
                context.put(atomKeyMapping.get(entry.getKey()), DateUtil.format(DateUtil.date(), DatePattern.NORM_DATETIME_PATTERN));
            } else {
                context.put(atomKeyMapping.get(entry.getKey()), "testkey");
            }
        });


        try {
            String qlExpress = RuleConfigUtil.genRuleRunExpress(ruleTree, atomKeyMapping);
            log.info("校验规则=======>生成执行表达式为{}", qlExpress);
            log.info("校验规则=======>传入参数为{}", context);
            ExpressRunner runner = new ExpressRunner();
            runner.execute(qlExpress, context, null, true, false);
            afsRuleInfoDto.setValidate(Boolean.TRUE);
        } catch (Exception e) {
            log.error("规则校验失败", e);
            afsRuleInfoDto.setValidate(Boolean.FALSE);
        }
        afsRuleInfoDto.setRuleData(null);
        return afsRuleInfoDto;
    }

    /**
     * Check and save rule afs rule info dto
     * <p>Description: </p>
     *
     * @param afsRuleInfoDto afs rule info dto
     * @return the afs rule info dto
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public AfsRuleInfoDto checkAndSaveRule(AfsRuleInfoDto afsRuleInfoDto) {
        List<RuleTreeDto> ruleTree = RuleConfigUtil.genRuleItemTree(afsRuleInfoDto.getRuleData());
        Assert.isTrue(ruleTree != null && ruleTree.size() > 0, "规则内容为空");
        AfsRuleInfo oldDbAfsRuleInfo = this.getOne(Wrappers.<AfsRuleInfo>lambdaQuery().eq(AfsRuleInfo::getRuleNo, afsRuleInfoDto.getRuleNo()).orderByDesc(AfsRuleInfo::getRuleVersion), false);
        Assert.isTrue(!(oldDbAfsRuleInfo != null && afsRuleInfoDto.getId() == null), "规则编号不能重复");
        if (afsRuleInfoDto.getId() == null) {
            AfsRuleInfo dbAfsRuleInfo = new AfsRuleInfo();
            BeanUtils.copyProperties(afsRuleInfoDto, dbAfsRuleInfo);
            dbAfsRuleInfo.setRuleVersion(0);
            dbAfsRuleInfo.setSortOrder(afsRuleInfoDto.getSortOrder());
            dbAfsRuleInfo.setStatus(RuleStatus.NEW);
            this.save(dbAfsRuleInfo);
            afsRuleInfoDto.setId(dbAfsRuleInfo.getId());
            afsRuleOperateHisService.createRule(dbAfsRuleInfo.getId());
        } else {
            Assert.isTrue(oldDbAfsRuleInfo != null, MessageFormat.format("规则[{0}]提交数据数据库状态错误", afsRuleInfoDto.getRuleName()));
            //提交规则ID与数据库生效ID不同，返回失败不允许当前编辑
            Assert.isTrue(afsRuleInfoDto.getId() != oldDbAfsRuleInfo.getId(), MessageFormat.format("规则[{0}]提交数据与数据库版本不一致", oldDbAfsRuleInfo.getRuleName()));
            if (oldDbAfsRuleInfo.getStatus() == RuleStatus.NEW) {
                oldDbAfsRuleInfo.setRuleGroup(afsRuleInfoDto.getRuleGroup());
                oldDbAfsRuleInfo.setRuleName(afsRuleInfoDto.getRuleName());
                oldDbAfsRuleInfo.setActiveStartDate(afsRuleInfoDto.getActiveStartDate());
                oldDbAfsRuleInfo.setActiveEndDate(afsRuleInfoDto.getActiveEndDate());
                oldDbAfsRuleInfo.setSortOrder(afsRuleInfoDto.getSortOrder());
                afsRuleOperateHisService.modifyRule(oldDbAfsRuleInfo.getId(), oldDbAfsRuleInfo.getId(), RuleOperateType.MODIFY);
                this.updateById(oldDbAfsRuleInfo);
            } else {
                AfsRuleInfo afsRuleInfo = new AfsRuleInfo();
                BeanUtils.copyProperties(afsRuleInfoDto, afsRuleInfo);
                afsRuleInfo.setId(null);
                afsRuleInfo.setRuleVersion(oldDbAfsRuleInfo.getRuleVersion() + 1);
                afsRuleInfo.setSortOrder(afsRuleInfoDto.getSortOrder());
                afsRuleInfo.setRuleGroup(afsRuleInfoDto.getRuleGroup());
                afsRuleInfo.setRuleName(afsRuleInfoDto.getRuleName());
                afsRuleInfo.setActiveStartDate(afsRuleInfoDto.getActiveStartDate());
                afsRuleInfo.setActiveEndDate(afsRuleInfoDto.getActiveEndDate());
                afsRuleInfo.setSortOrder(afsRuleInfoDto.getSortOrder());
                afsRuleInfo.setStatus(RuleStatus.NEW);
                afsRuleOperateHisService.newVersion(oldDbAfsRuleInfo.getId(), afsRuleInfo.getId());
                this.save(afsRuleInfo);
                afsRuleInfoDto.setId(afsRuleInfo.getId());
            }
        }

        //保存AfsRuleItem 信息
        afsRuleItemMapper.delete(Wrappers.<AfsRuleItem>lambdaQuery().eq(AfsRuleItem::getRuleId, afsRuleInfoDto.getId()));
        Map<String, RuleTreeExpressAtomDto> atomDtoMap = new HashMap<>();
        Map<String, String> atomKeyMapping = new HashMap<>();
        for (int i = 0; i < ruleTree.size(); i++) {
            AfsRuleItem afsRuleItem = new AfsRuleItem();
            afsRuleItem.setRuleId(afsRuleInfoDto.getId());
            afsRuleItem.setItemType(ruleTree.get(i).getType());
            afsRuleItem.setItemLabel(ruleTree.get(i).getLabel());
            afsRuleItem.setItemKey(ruleTree.get(i).getKey());
            afsRuleItem.setItemNot(ruleTree.get(i).isNot());
            afsRuleItem.setItemSortOrder(i);
            afsRuleItem.setParentId(CommonConstants.PARENT_ID);
            afsRuleItemMapper.insert(afsRuleItem);
            processItemExpress(ruleTree.get(i).getExpress(), afsRuleInfoDto.getId(), afsRuleItem.getId(), atomDtoMap, true);
            processAfsRuleItem(ruleTree.get(i), afsRuleInfoDto.getId(), afsRuleItem.getId(), atomDtoMap, true);
        }

        //保存值映射
        afsRuleInputMappingMapper.delete(Wrappers.<AfsRuleInputMapping>lambdaQuery().eq(AfsRuleInputMapping::getRuleId, afsRuleInfoDto.getId()));
        atomDtoMap.entrySet().forEach(entry -> {
            AfsRuleInputMapping afsRuleInputMapping = new AfsRuleInputMapping();
            afsRuleInputMapping.setAtomKey(entry.getKey());
            afsRuleInputMapping.setAtomName(entry.getValue().getName());
            afsRuleInputMapping.setAtomType(entry.getValue().getComponent());
            afsRuleInputMapping.setRuleId(afsRuleInfoDto.getId());
            afsRuleInputMapping.setValueExpress(entry.getValue().getValueExpress());
            afsRuleInputMapping.setMappingKey("R_"+entry.getKey().replaceAll("\\.", "_") + "_" + RandomStringUtils.randomAlphanumeric(8));
            if (entry.getValue().getComponent() == AtomComponentType.NUMBER) {
                afsRuleInputMapping.setAtomScale(entry.getValue().getComponentConfig().getInteger("scale"));
            } else {
                afsRuleInputMapping.setAtomScale(0);
            }

            if (entry.getValue().getComponent() == AtomComponentType.DATE) {
                afsRuleInputMapping.setAtomFormat(entry.getValue().getComponentConfig().getString("format"));
            } else {
                afsRuleInputMapping.setAtomFormat("");
            }
            atomKeyMapping.put(entry.getKey(), afsRuleInputMapping.getMappingKey());
            afsRuleInputMappingMapper.insert(afsRuleInputMapping);
        });
        //ql 执行表达式保存
        afsRuleTransExpressMapper.delete(Wrappers.<AfsRuleTransExpress>lambdaQuery().eq(AfsRuleTransExpress::getRuleId, afsRuleInfoDto.getId()));
        AfsRuleTransExpress afsRuleTransExpress = new AfsRuleTransExpress();
        afsRuleTransExpress.setQlExpress(RuleConfigUtil.genRuleRunExpress(ruleTree, atomKeyMapping));
        afsRuleTransExpress.setRuleId(afsRuleInfoDto.getId());
        afsRuleTransExpress.setSaveVersion(System.currentTimeMillis());
        if (StringUtils.isNotEmpty(afsRuleTransExpress.getQlExpress())) {
            afsRuleTransExpressMapper.insert(afsRuleTransExpress);
        }
        saveRuleOut(afsRuleInfoDto);
        afsRuleInfoDto.setRuleData(null);
        return afsRuleInfoDto;
    }


    @Override
    public void saveRuleOut(AfsRuleInfoDto afsRuleInfoDto) {
        if (StringUtils.isNoneEmpty(afsRuleInfoDto.getRuleOut())) {
            afsRuleResultMappingMapper.delete(Wrappers.<AfsRuleResultMapping>lambdaQuery().eq(AfsRuleResultMapping::getRuleId, afsRuleInfoDto.getId()));
            AfsRuleResultMapping afsRuleResultMapping = new AfsRuleResultMapping();
            afsRuleResultMapping.setRuleId(afsRuleInfoDto.getId());
            afsRuleResultMapping.setResult(afsRuleInfoDto.getRuleOut());
            afsRuleResultMappingMapper.insert(afsRuleResultMapping);
        }

    }

    @Override
    public List<RuleTreeDto> loadRuleTree(Long ruleId) {
        List<AfsRuleItem> items = afsRuleItemMapper
                .selectList(Wrappers.<AfsRuleItem>lambdaQuery().eq(AfsRuleItem::getRuleId, ruleId));

        Map<Long, List<RuleTreeDto>> parentIdMap = new HashMap<>();
        Set<Long> itemIdSet = new HashSet<>();
        List<RuleTreeDto> ruleTreeDtoList = items
                .stream()
                .map(afsRuleItem -> {
                    RuleTreeDto ruleTreeDto = new RuleTreeDto();
                    ruleTreeDto.setId(afsRuleItem.getId());
                    ruleTreeDto.setRuleId(ruleId);
                    ruleTreeDto.setType(afsRuleItem.getItemType());
                    ruleTreeDto.setLabel(afsRuleItem.getItemLabel());
                    ruleTreeDto.setKey(afsRuleItem.getItemKey());
                    ruleTreeDto.setNot(afsRuleItem.isItemNot());
                    ruleTreeDto.setParentId(afsRuleItem.getParentId());
                    ruleTreeDto.setRows(new ArrayList<>());
                    ruleTreeDto.setExpress(null);
                    if (!parentIdMap.containsKey(afsRuleItem.getParentId())) {
                        parentIdMap.put(afsRuleItem.getParentId(), new ArrayList<>());
                    }
                    parentIdMap.get(afsRuleItem.getParentId()).add(ruleTreeDto);
                    return ruleTreeDto;
                }).collect(Collectors.toList());

        ruleTreeDtoList.forEach(ruleTreeDto -> {
            ruleTreeDto.setRows(parentIdMap.get(ruleTreeDto.getId()));
            itemIdSet.add(ruleTreeDto.getId());
        });

        Map<Long, AfsRuleExpress> idExpressMap = new HashMap<>();
        Map<Long, Long> itemIdExpressIdMap = new HashMap<>();

        afsRuleExpressMapper
                .selectList(Wrappers.<AfsRuleExpress>lambdaQuery().eq(AfsRuleExpress::getRuleId, ruleId).in(itemIdSet.size() > 0, AfsRuleExpress::getItemId, itemIdSet))
                .forEach(afsRuleExpress -> {
                    idExpressMap.put(afsRuleExpress.getId(), afsRuleExpress);
                    // 父级express id 永远最小，因此要找到最小的映射
                    if (!itemIdExpressIdMap.containsKey(afsRuleExpress.getItemId())) {
                        itemIdExpressIdMap.put(afsRuleExpress.getItemId(), afsRuleExpress.getId());
                    } else {
                        if (afsRuleExpress.getId() < itemIdExpressIdMap.get(afsRuleExpress.getItemId())) {
                            itemIdExpressIdMap.put(afsRuleExpress.getItemId(), afsRuleExpress.getId());
                        }
                    }
                });


        Map<Long, Map<String, AfsRuleExpressAtom>> expressIdAtomsMap = new HashMap<>();

        afsRuleExpressAtomMapper
                .selectList(Wrappers.<AfsRuleExpressAtom>lambdaQuery().eq(AfsRuleExpressAtom::getRuleId, ruleId).in(idExpressMap.keySet().size() > 0, AfsRuleExpressAtom::getExpressId, idExpressMap.keySet()))
                .forEach(afsRuleExpressAtom -> {
                    if (!expressIdAtomsMap.containsKey(afsRuleExpressAtom.getExpressId())) {
                        expressIdAtomsMap.put(afsRuleExpressAtom.getExpressId(), new HashMap<>());
                    }
                    expressIdAtomsMap
                            .get(afsRuleExpressAtom.getExpressId())
                            .put(AfsEnumUtil.key(afsRuleExpressAtom.getAtomLocation()), afsRuleExpressAtom);
                });

        ruleTreeDtoList.forEach(ruleTreeDto -> {
            if (itemIdExpressIdMap.containsKey(ruleTreeDto.getId())) {
                ruleTreeDto.setExpress(genRuleTreeExpress(idExpressMap.get(itemIdExpressIdMap.get(ruleTreeDto.getId())), idExpressMap, expressIdAtomsMap));
            }
        });


        return parentIdMap.getOrDefault(CommonConstants.PARENT_ID, new ArrayList<>());
    }

    private RuleTreeExpressDto genRuleTreeExpress(AfsRuleExpress express, Map<Long, AfsRuleExpress> idExpressMap, Map<Long, Map<String, AfsRuleExpressAtom>> expressIdAtomsMap) {
        RuleTreeExpressDto ruleTreeExpressDto = new RuleTreeExpressDto();
        RuleTreeExpressLeftDto left = new RuleTreeExpressLeftDto();
        left.setType(express.getLeftType());
        if (expressIdAtomsMap.containsKey(express.getId()) && expressIdAtomsMap.get(express.getId()).containsKey(AfsEnumUtil.key(AtomLocation.LEFT))) {
            left.setAtom(genRuleTreeExpressAtom(expressIdAtomsMap.get(express.getId()).get(AfsEnumUtil.key(AtomLocation.LEFT))));
        }
        if (express.getLeftReferenceId() != null) {
            left.setExpress(genRuleTreeExpress(idExpressMap.get(express.getLeftReferenceId()), idExpressMap, expressIdAtomsMap));
        }

        ruleTreeExpressDto.setLeft(left);

        ruleTreeExpressDto.setOperator(JSONObject.parseObject(express.getOperatorInfo(), RuleTreeExpressOperatorDto.class));

        RuleTreeExpressRightDto right = new RuleTreeExpressRightDto();

        right.setType(express.getRightType());
        right.setValue(express.getRightValue());
        right.setValueNames(express.getRightValueNames());

        if (expressIdAtomsMap.containsKey(express.getId()) && expressIdAtomsMap.get(express.getId()).containsKey(AfsEnumUtil.key(AtomLocation.RIGHT))) {
            right.setAtom(genRuleTreeExpressAtom(expressIdAtomsMap.get(express.getId()).get(AfsEnumUtil.key(AtomLocation.RIGHT))));
        }

        if (express.getRightReferenceId() != null) {
            right.setExpress(genRuleTreeExpress(idExpressMap.get(express.getRightReferenceId()), idExpressMap, expressIdAtomsMap));
        }

        ruleTreeExpressDto.setRight(right);
        return ruleTreeExpressDto;
    }

    private RuleTreeExpressAtomDto genRuleTreeExpressAtom(AfsRuleExpressAtom afsRuleExpressAtom) {
        return JSONObject.parseObject(afsRuleExpressAtom.getAtomInfo(), RuleTreeExpressAtomDto.class);
    }

    @Override
    public boolean activeRule(Long ruleId) {
        try {
            afsRulePublishService.active(ruleId);
        } catch (Exception e) {
            throw e;
        }
        stringRedisTemplate.opsForValue().set(RuleConstants.RULE_SYNC_TIMESTAMP,System.currentTimeMillis()+"");
        ThreadUtil.execute(() -> SpringContextHolder.getBean(RulePublishComponent.class).publishRule(ServiceInfo.getServiceName(),true));
        return true;
    }

    @Override
    public <T, R> boolean activeRule(Long ruleId, Function<T, R> function, T param) {
        try {
            afsRulePublishService.active(ruleId);
        } catch (Exception e) {
            throw e;
        }
        publishRuleAndRunSync(function,param);
        return true;
    }



    @Override
    public boolean activeRuleWithoutPublish(Long ruleId) {
        afsRulePublishService.active(ruleId);
        return true;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean changeRuleDate(Long ruleId, Date startDate, Date endDate) {
        AfsRuleInfo afsRuleInfo = this.getById(ruleId);
        Assert.isTrue(afsRuleInfo != null, "输入规则编号不存在");
        Assert.isTrue(RuleStatus.ACTIVE != afsRuleInfo.getStatus(), "规则已激活不允许修改");
        afsRuleInfo.setActiveStartDate(startDate == null ? null : (DateUtil.format(startDate, DatePattern.PURE_DATETIME_PATTERN)));
        afsRuleInfo.setActiveEndDate(endDate == null ? null : (DateUtil.format(endDate, DatePattern.PURE_DATETIME_PATTERN)));
        afsRuleOperateHisService.modifyRule(afsRuleInfo.getId(), afsRuleInfo.getId(), RuleOperateType.CHANGE_DATE);
        this.updateById(afsRuleInfo);
        return true;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean expiredRule(Long ruleId) {
        AfsRuleInfo afsRuleInfo = this.getById(ruleId);
        Assert.isTrue(afsRuleInfo != null, "输入规则编号不存在");
        Assert.isTrue(RuleStatus.ACTIVE != afsRuleInfo.getStatus(), "规则已激活不允许做过期操作");
        afsRuleOperateHisService.expiredRule(ruleId);
        afsRuleInfo.setStatus(RuleStatus.EXPIRED);
        this.updateById(afsRuleInfo);
        return true;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean deleteRule(Long ruleId) {
        AfsRuleInfo afsRuleInfo = this.getById(ruleId);
        Assert.isTrue(afsRuleInfo != null, "输入规则编号不存在");
        Assert.isTrue(RuleStatus.ACTIVE != afsRuleInfo.getStatus(), "规则已激活不允许删除");
        afsRuleOperateHisService.modifyRule(ruleId, ruleId, RuleOperateType.DELETE);
        this.removeById(ruleId);
        return true;
    }

    @Override
    public boolean deActiveRule(Long ruleId) {
        try {
            afsRulePublishService.deActive(ruleId);
        } catch (Exception e) {
            throw e;
        }
        stringRedisTemplate.opsForValue().set(RuleConstants.RULE_SYNC_TIMESTAMP,System.currentTimeMillis()+"");
        ThreadUtil.execute(() -> SpringContextHolder.getBean(RulePublishComponent.class).publishRule(ServiceInfo.getServiceName(),true));
        return true;
    }

    @Override
    public <T, R> boolean deActiveRule(Long ruleId, Function<T, R> function, T param) {
        try {
            afsRulePublishService.deActive(ruleId);
        } catch (Exception e) {
            throw e;
        }
        publishRuleAndRunSync(function,param);
        return true;
    }
    @Override
    public boolean deActiveRuleWithoutPublish(Long ruleId) {
        afsRulePublishService.deActive(ruleId);
        return true;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long copyRule(AfsRuleCopyDto afsRuleCopyDto) {
        List<RuleTreeDto> oldList = this.loadRuleTree(afsRuleCopyDto.getSourceId());
        AfsRuleInfoDto afsRuleInfoDto = new AfsRuleInfoDto();
        afsRuleInfoDto.setId(null);
        afsRuleInfoDto.setRuleData(JSONObject.toJSONString(oldList));
        afsRuleInfoDto.setRuleGroup(afsRuleCopyDto.getToRuleGroup());
        afsRuleInfoDto.setRuleName(afsRuleCopyDto.getToRuleName());
        afsRuleInfoDto.setRuleNo(afsRuleCopyDto.getToRuleNo());
        afsRuleInfoDto.setSortOrder(0);
        this.checkAndSaveRule(afsRuleInfoDto);
        AfsRuleResultMapping afsRuleResultMapping = afsRuleResultMappingMapper.selectOne(Wrappers.<AfsRuleResultMapping>lambdaQuery().eq(AfsRuleResultMapping::getRuleId, afsRuleCopyDto.getSourceId()));
        if (afsRuleResultMapping != null) {
            afsRuleResultMapping.setId(null);
            afsRuleResultMapping.setRuleId(afsRuleInfoDto.getId());
            afsRuleResultMapping.setDelFlag(CommonConstants.STATUS_NORMAL);
            afsRuleResultMapping.setCreateBy(null);
            afsRuleResultMapping.setCreateTime(null);
            afsRuleResultMapping.setUpdateBy(null);
            afsRuleResultMapping.setUpdateTime(null);
            afsRuleResultMappingMapper.insert(afsRuleResultMapping);
        }
        return afsRuleInfoDto.getId();
    }

    private <T, R>  void publishRuleAndRunSync( Function<T, R> function, T param){
        ThreadUtil.execute(()->{
            try {
                SpringContextHolder.getBean(RulePublishComponent.class).publishRule(ServiceInfo.getServiceName(), true);
                R result =   function.apply(param);
                log.info("规则后置事件完成,运行结果[{}]",result);
            }catch (Throwable e){
                log.error("",e);
            }
        });
    }
}
